/*
 * Values.java
 * 
 * This file is part of Netuno Psamata.
 * 
 * CajuScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3, or (at your option) 
 * any later version.
 * 
 * CajuScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with CajuScript.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.netuno.psamata;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Properties;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.File;
import java.io.IOException;

/**
 * Values, parameters management.
 * @author eduveks
 */
public class Values implements java.io.Serializable {
    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
     * Reference to Original Keys.
     */
    private Map<String, String> keysRef = new HashMap<String, String>();
    /**
     * Objects Values.
     */
    private Map<String, Object> objects = new HashMap<String, Object>();
    /**
     * Url Character Encoding.
     */
    private static String urlCharacterEncoding = "UTF-8";
    /**
     * Values, parameters management.
     */
    public Values() { }
    /**
     * Values, parameters management.
     * @param variablesLine Line with variables
     * @param splitter Splitter of the variables
     * @param set Sign of equal
     */
    public Values(final String variablesLine, final String splitter,
            final String set) {
        values(variablesLine, splitter, set, "UTF-8");
    }
    /**
     * Values, parameters management.
     * @param variablesLine Line with variables
     * @param splitter Splitter of the variables
     * @param set Sign of equal
     */
    public Values(final String variablesLine, final String splitter,
            final String set, final String charsetName) {
    	values(variablesLine, splitter, set, charsetName);
    }
    
    private void values(final String variablesLine, final String splitter,
            final String set, final String charsetName) {
    	if (charsetName != null && !charsetName.equals("")) {
            urlCharacterEncoding = charsetName;
        }
        if (variablesLine != null && !variablesLine.equals("")) {
            String[] variables = variablesLine.split(splitter);
            for (int x = 0; x < variables.length; x++) {
                if (variables[x].indexOf(set) == -1) {
                    set(variables[x], null);
                } else if ((!variables[x].equals("")) && (variables[x] != null)
                        && (!variables[x].equals(set))) {
                    String[] part = variables[x].split(set, 2);
                    if (part[1].equals("")) {
                        set(part[0], null);
                    } else {
                        try {
                        	set(part[0], URLDecoder.decode(part[1],
                                    urlCharacterEncoding));
                        } catch (Exception e) {
                            set(part[0], part[1]);
                        }
                    }
                }
            }
        }
    }
    
    /**
     * Get Character Encoding for Url Encode and Decode.
     * @return url character encoding
     */
    public static String getUrlCharacterEncoding() {
        return urlCharacterEncoding;
    }
    /**
     * Set Character Encoding for Url Encode and Decode.
     * @param characterEncoding url character encoding
     */
    public static void setUrlCharacterEncoding(final String characterEncoding) {
        urlCharacterEncoding = characterEncoding;
    }
    /**
     * Get Object.
     * @param key Key
     * @return object found
     */
    public final Object get(final String key) {
        try {
            return objects.get(keysRef.get(key.toUpperCase()));
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * Get Object as String.
     * @param key Key
     * @return string found
     */
    public final String getString(final String key) {
        return getString(key, null);
    }
    /**
     * Get Object as String defining the Charset.
     * @param key Key
     * @param charsetName Charset
     * @return string found
     */
    public final String getString(final String key, final String charsetName) {
        try {
            Object obj = get(key);
            String result = "";
            if (obj == null) {
                result = "";
            } else if (obj instanceof java.io.InputStream) {
                result = org.netuno.psamata.io.InputStream.readAll((java.io.InputStream)obj);
            } else if (obj instanceof java.io.Reader) {
                result = org.netuno.psamata.io.InputStream.readAll((java.io.Reader)obj);
            } else {
                result = obj.toString();
            }
            if (charsetName != null && !charsetName.equals("")) {
                byte[] b = result.getBytes();
                result = new String(b, 0, b.length, charsetName);
            }
            return "" + result;
        } catch (Exception e) {
            return "";
        }
    }
    
    public final String getHtmlEncode(final String key) {
        String value = getString(key);
        return org.apache.commons.lang3.StringEscapeUtils.escapeHtml4(value);
    }
    
    public final String getHtmlDecode(final String key) {
        String value = getString(key);
        return org.apache.commons.lang3.StringEscapeUtils.unescapeHtml4(value);
    }
    
    /**
     * Get Object as Int.
     * @param key Key
     * @return int found
     */
    public final int getInt(final String key) {
        try {
            return Integer.valueOf(toString(get(key))).intValue();
        } catch (Exception e) {
            return -1;
        }
    }
    /**
     * Get Object as Float.
     * @param key Key
     * @return float found
     */
    public final float getFloat(final String key) {
        try {
        	if (get(key) == null) {
        		return -1;
        	}
            return Float.valueOf(toString(get(key))).floatValue();
        } catch (Exception e) {
            return -1;
        }
    }
    /**
     * Get Object as Double.
     * @param key Key
     * @return double found
     */
    public final double getDouble(final String key) {
        try {
            return Double.valueOf(toString(get(key))).doubleValue();
        } catch (Exception e) {
            return -1;
        }
    }
    /**
     * Get Object as Long.
     * @param key Key
     * @return long found
     */
    public final long getLong(final String key) {
        try {
            return Long.valueOf(toString(get(key))).longValue();
        } catch (Exception e) {
            return -1;
        }
    }
    /**
     * Get Object as Boolean.
     * @param key Key
     * @return boolean found
     */
    public final boolean getBoolean(final String key) {
        try {
            return Boolean.valueOf(toString(get(key))).booleanValue();
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * Get Object as Date.
     * @param key Key
     * @return Date found
     */
    public final java.util.Date getDate(final String key) {
        try {
            return (java.util.Date)get(key);
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * Get Object as Psamata File.
     * @param key Key
     * @return Psamata File found
     */
    public final org.netuno.psamata.io.File getFile(final String key) {
        try {
            return (org.netuno.psamata.io.File)get(key);
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * Set Object, create or update.
     * @param key key
     * @param value value
     */
    public final void set(final String key, final Object value) {
    	keysRef.put(key.toUpperCase(), key);
        objects.put(key, value);
    }
    /**
     * Has key.
     * @param key key
     */
    public final boolean hasKey(final String key) {
        try {
        	return objects.keySet().contains(keysRef.get(key.toUpperCase()));
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * Remove Object.
     * @param key key
     */
    public final void remove(final String key) {
        try {
            objects.remove(keysRef.get(key.toUpperCase()));
        } catch (Exception e) {
            return;
        }
    }
    /**
     * Get all keys.
     * @return Keys
     */
    public final Set<String> getKeys() {
        return objects.keySet();
    }
    /**
     * Get all keys sorted.
     * @return Keys
     */
    public final Set<String> getKeysSorted() {
    	return new TreeSet<String>(getKeys());
    }
    /**
     * Get Keys.
     * @param splitter Splitter
     * @return All keys with splitter
     */
    public final String getKeys(final String splitter) {
        return getKeys(objects, splitter);
    }
    /**
     * Get Keys.
     * @param map Collecion implements java.util.Map
     * @param splitter Splitter
     * @return All keys with splitter
     */
    public static String getKeys(final Map<String, Object> map, final String splitter) {
        String result = "";
        int counter = 0;
        for (String key : map.keySet()) {
            if (key != null && !key.equals("")) {
                counter++;
                if (counter > 1) {
                    result += splitter;
                }
                result += key;
            }
        }
        return result;
    }
    /**
     * Get Values.
     * @param splitter splitter
     * @return All values with splitter
     */
    public final String getValues(final String splitter) {
        return getValues(objects, splitter, false);
    }
    /**
     * Get Values.
     * @param splitter splitter
     * @param modeUrlEncode url encode
     * @return All values with splitter
     */
    public final String getValues(final String splitter,
            final boolean modeUrlEncode) {
        return getValues(objects, splitter, modeUrlEncode, null);
    }
    /**
     * Get Values.
     * @param splitter splitter
     * @param modeUrlEncode url encode
     * @return All values with splitter
     */
    public final String getValues(final String splitter, final boolean modeUrlEncode, final String[] excludes) {
        return getValues(objects, splitter, modeUrlEncode, excludes);
    }
    /**
     * Get Values.
     * @param map Collecion implements java.util.Map
     * @param splitter splitter
     * @param modeUrlEncode url encode
     * @return All values with splitter
     */
    public static String getValues(final Map<String, Object> map, final String splitter, final boolean modeUrlEncode) {
    	return getValues(map, splitter, modeUrlEncode, null);
    }
    /**
     * Get Values.
     * @param map Collection implements java.util.Map
     * @param splitter splitter
     * @param modeUrlEncode url encode
     * @return All values with splitter
     */
    public static String getValues(final Map<String, Object> map, final String splitter, final boolean modeUrlEncode, final String[] excludes) {
        String result = "";
        int counter = 0;
        keys: for (String key : map.keySet()) {
            if (excludes != null) {
                for (String keyExcludes : excludes) {
                    if (key.equalsIgnoreCase(keyExcludes)) {
                        continue keys;
                    }
                }
            }
            if (!key.equals("")) {
                String value = map.get(key) == null ? "" : map.get(key).toString();
                try {
                    if (modeUrlEncode) {
                        value = URLEncoder.encode(value, urlCharacterEncoding);
                    }
                } catch (Exception e) { }
                counter++;
                if (counter > 1) {
                    result += splitter;
                }
                result += value;
            }
        }
        return result;
    }
    /**
     * Get In Format.
     * @param splitter Splitter of the variables
     * @param set Sign of equal
     * @return Formatted values
     */
    public final String getInFormat(final String splitter, final String set) {
        return getInFormat(objects, splitter, set, false, null);
    }
    /**
     * Get In Format.
     * @param splitter Splitter of the variables
     * @param set Sign of equal
     * @param excludes Keys to be excludes
     * @return Formatted values
     */
    public final String getInFormat(final String splitter, final String set,
            final String[] excludes) {
        return getInFormat(objects, splitter, set, false, excludes);
    }
    /**
     * Get In Format.
     * @param splitter Splitter of the variables
     * @param set Sign of equal
     * @param modeUrlEncode Using the url encode
     * @return Formatted values
     */
    public final String getInFormat(final String splitter, final String set,
            final boolean modeUrlEncode) {
        return getInFormat(objects, splitter, set, modeUrlEncode, null);
    }
    /**
     * Get In Format.
     * @param map Collecion implements java.util.Map
     * @param splitter Splitter of the variables
     * @param set Sign of equal
     * @param modeUrlEncode Using the url encode
     * @param excludes Keys to be excludes
     * @return Formatted values
     */
    public static String getInFormat(final Map<String, Object> map, final String splitter, final String set, final boolean modeUrlEncode, final String[] excludes) {
        String result = "";
        int counter = 0;
        keys: for (String key : map.keySet()) {
            if (excludes != null) {
                for (String keyExcludes : excludes) {
                    if (key.equalsIgnoreCase(keyExcludes)) {
                        continue keys;
                    }
                }
            }
            String value = "";
            try {
                if (modeUrlEncode) {
                    value = URLEncoder.encode(map.get(key).toString(), urlCharacterEncoding);
                } else {
                    value = map.get(key).toString();
                }
            } catch (Exception e) {
                result += "";
            }
            if (!key.equals("")) {
                counter++;
                if (counter > 1) {
                    result += splitter;
                }
                result += key + set + value;
            }
        }
        return result;
    }
    /**
     * Search, find content in values.
     * @param content content to find
     * @return Key found the content
     */
    public final String search(final String content) {
        return search(objects, content, "", true);
    }
    /**
     * Search, find content in values.
     * @param content content to find
     * @param ignoreCase if it is false is case sensitive else not
     * @return Key found the content
     */
    public final String search(final String content, final boolean ignoreCase) {
        return search(objects, content, "", ignoreCase);
    }
    /**
     * Search, find content in values.
     * @param content content to find
     * @param splitter splitter of content to find
     * @return Key found the content
     */
    public final String search(final String content, final String splitter) {
        return search(objects, content, splitter, true);
    }
    /**
     * Search, find content in values.
     * @param content content to find
     * @param splitter splitter of content to find
     * @param ignoreCase if it is false is case sensitive else not
     * @return Key found the content
     */
    public final String search(final String content, final String splitter,
            final boolean ignoreCase) {
        return search(objects, content, splitter, ignoreCase);
    }
    /**
     * Search, find content in values.
     * @param map map to find
     * @param content content to find
     * @param splitter splitter of content to find
     * @param ignoreCase if it is false is case sensitive else not
     * @return Key found the content
     */
    public static String search(final Map<String, Object> map, final String content, final String splitter, final boolean ignoreCase) {
        for (String key : map.keySet()) {
            String value = map.get(key) == null ? "" : map.get(key).toString();
            if (ignoreCase) {
                if ((splitter + value + splitter).toUpperCase().indexOf((splitter + content + splitter).toUpperCase()) > -1) {
                    return key;
                }
            } else {
                if ((splitter + value + splitter).indexOf(splitter + content + splitter) > -1) {
                    return key;
                }
            }
        }
        return null;
    }

    public final String toString(Object object) {
    	return object == null ? "" : object.toString();
    }
    
    public final void importFrom(Values values) {
    	for (String key : values.getKeys()) {
    		set(key, values.get(key));
    	}
    }
    
    /**
     * Load values from input.
     * @param in Input
     * @throws IOException Read exception
     */
    public final void load(final java.io.InputStream in) throws IOException {
    	Properties properties = new Properties();
    	properties.load(in);
    	Iterator<Object> i = properties.keySet().iterator();
        while (i.hasNext()) {
            String key = (String) i.next();
            String value = properties.getProperty(key);
            if (!key.equals("")) {
                set(key.toUpperCase(), value);
            }
        }
    }
    /**
     * Load values from input.
     * @param in Input
     * @throws IOException Read exception
     */
    public final void load(final java.io.Reader in) throws IOException {
    	Properties properties = new Properties();
    	properties.load(in);
    	Iterator<Object> i = properties.keySet().iterator();
        while (i.hasNext()) {
            String key = (String) i.next();
            String value = properties.getProperty(key);
            if (!key.equals("")) {
                set(key.toUpperCase(), value);
            }
        }
    }
    /**
     * Load values from string.
     * @param data String data
     * @throws IOException Read exception
     */
    public final void loadFromString(final String data) throws IOException {
    	java.io.StringReader in = new java.io.StringReader(data);
        load(in);
    }
    /**
     * Load values in file.
     * @param file File path
     * @throws IOException Read file exception
     */
    public final void loadFile(final File file) throws IOException {
        InputStream fileIn = new FileInputStream(file.getAbsoluteFile());
        load(fileIn);
        fileIn.close();
    }
    /**
     * Save in file.
     * @param file File path
     * @throws IOException Write file exception
     */
    public final void saveFile(final File file) throws IOException {
        OutputStream fileOut = new FileOutputStream(file.getAbsoluteFile());
        save(fileOut);
        fileOut.close();
    }
    /**
     * Save in output.
     * @param out Output
     * @throws IOException Write exception
     */
    public final void save(final java.io.OutputStream out) throws IOException {
    	Properties properties = new Properties();
        for (String key : objects.keySet()) {
        	properties.put(key, objects.get(key) == null ? "" : objects.get(key).toString());
        }
        properties.store(out, "Netuno Psamata Values");
    }
    /**
     * Save in output.
     * @param out Output
     * @throws IOException Write exception
     */
    public final void save(final java.io.Writer out) throws IOException {
    	Properties properties = new Properties();
        for (String key : objects.keySet()) {
        	properties.put(key, objects.get(key) == null ? "" : objects.get(key).toString());
        }
        properties.store(out, "Netuno Psamata Values");
    }
    /**
     * Save as string.
     * @return Output
     * @throws IOException Write exception
     */
    public final String saveToString() throws IOException {
    	java.io.StringWriter sw = new java.io.StringWriter();
        save(sw);
        return sw.toString();
    }
    /**
     * Remove all keys and values, clear all, erase all.
     */
    public final void removeAll() {
        objects.clear();
    }
    /**
     * Finalize.
     * @throws Throwable Throwable
     */
    @Override
    protected final void finalize() throws Throwable {
        removeAll();
    }
    /*
    private void readObject(ObjectInputStream objectInputStream) throws ClassNotFoundException, IOException {
    	load(objectInputStream);
    }

    private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
    	save(objectOutputStream);
    }
    */
}